/*
 * Copyright 2003-2005 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */

#include "../Types.h"

#include "../dictionary/Dictionary.h"

#include "../common/BigEndianUnsignedShort.h"
#include "../common/BigEndianUnsignedByte.h"
#include "../meta/MetaName.h"
#include "../meta/MetaExpression.h"
#include "../meta/MetaEnvelop.h"

#include "../util/buffer.h"


#include <stdio.h>

static int readCore( type_library * library, type_input_stream * tmis );
static int readMessageMap( type_input_stream * tmis, reference_type_map * coreMap, reference_type_map * mapSpec );
static int setMap( type_input_stream * dictDataIn, reference_type_map * coreMap, type_map * mapSpec );
static type_buffer * writeCore( reference_type_map * map );

typedef struct type_triple
{
	int id;
	type_name * name;
	type_envelop * structure;
	bool reserved;
	type_element * definition;
}
type_triple;


int read_message( type_library * library, argot_read reader, void * readerData, void ** object ) 
{
	reference_type_map core;
	type_input_stream tmis;
	reference_type_map messageMap;
	int rc;
	uint16_t id;

	// Create the basic core map.
	reference_type_map_init( &core, library, NULL );
	reference_type_map_set_reference( &core, &core.map );
	type_map_core_init( &core );		
	type_map_map( &core.map, 22, type_library_get_id( library, &dictionary_map_name ));
	type_map_map( &core.map, 23, type_library_get_id( library, &dictionary_words_name ));
	type_map_map( &core.map, 24, type_library_get_id( library, &dictionary_definition_name ));
	type_map_map( &core.map, 25, type_library_get_id( library, &dictionary_entry_name ));
	type_map_map( &core.map, 26, type_library_get_id( library, &meta_envelop_name ));
	type_map_map( &core.map, 27, type_library_get_id( library, &meta_definition_envelop_name ));
		 	
	type_input_init( &tmis, &core.map, reader, readerData );

	/* read the core map. */
	TRACE("READ CORE");
    rc = readCore( library, &tmis);
    if ( rc != TYPE_OK ) return rc;
    
    reference_type_map_init( &messageMap, library, NULL );
	reference_type_map_set_reference( &core, &messageMap.map );
	    
	/* read the message map using the core map. */
	TRACE("READ MESSAGE MAP");
	rc = readMessageMap( &tmis, &core, &messageMap );
	if ( rc != TYPE_OK ) return rc;
		
	/* read the final dictionary. register types if needed. */
    rc = type_input_read_named_object( &tmis, &uint16_name );
    if ( rc != TYPE_OK ) return rc;
    
    id = type_stack_get_u16( &tmis.stack );
	
	type_input_set_type_map( &tmis, &messageMap.map );

	rc = type_input_read_object( &tmis, id );
	if ( rc != TYPE_OK ) return rc;
	    
	*object = type_stack_get_ptr( &tmis.stack );
	return TYPE_OK;
}

static int readCore( type_library * library, type_input_stream * tmis )
{
	reference_type_map * core = (reference_type_map *) type_input_type_map( tmis );
	int x;
	type_input_stream buffer_stream;
	buffer_data reader_data;
	int rc;
		
	/* read the core array size.  expect = 2. */
	rc = type_input_read_named_object( tmis, &uint8_name );
	if ( rc != TYPE_OK ) return rc;
	uint16_t arraySize = type_stack_get_u8( type_input_stack( tmis ) );
	
	rc = type_input_read_named_object( tmis, &dictionary_words_name );
	if ( rc != TYPE_OK ) return rc;	
	type_envelop * readcore = (type_envelop *) type_stack_get_ptr( type_input_stack( tmis ) );	
	type_buffer * localCore = writeCore( core );
	if ( localCore == NULL ) return -1;
	
	if ( localCore == NULL ) return -1;
	
	if ( readcore->size != localCore->size || 0 != memcmp( readcore->data, localCore->data, readcore->size ) )
	{
		TRACE( "meta dictionary cores did not match" );
		
		int x = 0;
		
		for ( x=0; x< readcore->size; x++ )
		{
			if ( readcore->data[x] != localCore->data[x] )
			{
				printf( "mismatch @ %i, %i != %i\n", x, readcore->data[x], localCore->data[x] );
			}
		}
		/* FIX ME no magic numbers */
		return -1;
	}
		
	// now the core is confirmed we can add in the extensions.
	for ( x=0; x < arraySize-1; x++ )
	{
		type_input_read_named_object( tmis, &dictionary_words_name );
		type_envelop * extention = type_stack_get_ptr( type_input_stack( tmis ) );

		buffer_data_init( &reader_data, extention->data, extention->size );
		type_input_init( &buffer_stream, &core->map, bufferReader, &reader_data );
		
		rc = setMap( &buffer_stream, core, &core->map );
		if ( rc != TYPE_OK ) return rc;
	}
	
	return TYPE_OK;
}
	
static int readMessageMap( type_input_stream * tmis, reference_type_map * coreMap, reference_type_map * mapSpec )
{		
	int x;
	int rc;
	type_input_stream buffer_stream;
	buffer_data reader_data;
	
	/* read the core array size.  expect = 2. */
	type_input_read_named_object( tmis, &uint8_name );

	unsigned char arraySize = type_stack_get_u8( type_input_stack( tmis ) );
		
	// now the core is confirmed we can add in the extensions.
	for ( x=0; x < arraySize; x++ )
	{
		type_input_read_named_object( tmis, &dictionary_words_name );
		type_envelop * dict = (type_envelop *) type_stack_get_ptr( type_input_stack( tmis ) );

		buffer_data_init( &reader_data, dict->data, dict->size );
		type_input_init( &buffer_stream, &coreMap->map, bufferReader, &reader_data );
		
		rc = setMap( &buffer_stream, coreMap, &mapSpec->map );
		if ( rc != TYPE_OK ) return rc;
	}
	
	return TYPE_OK;
}

/* This reads the contents of the data dictionary and sets this
 * map up using the data contained.  It checks all data with the
 * internal library.
 */
static int setMap( type_input_stream * dictDataIn, reference_type_map * coreMap, type_map * mapSpec )
{
	unsigned short size;
	int x;
	int rc;
	type_triple ** newTypes;
	
	rc = type_input_read_named_object( dictDataIn, &uint16_name );
	if ( rc != TYPE_OK ) return rc;
	
	size = (int) type_stack_get_u16( type_input_stack( dictDataIn ) );
	

	newTypes = type_malloc( size * sizeof(type_triple *) );
	
	// Step 1.  Read all the types in.
	for ( x = 0; x<size; x++ )
	{
		type_triple * newType = type_malloc( sizeof( type_triple ) );
		
		type_input_read_named_object( dictDataIn, &uint16_name );
		newType->id = (int) type_stack_get_u16( type_input_stack( dictDataIn ) );
		
		type_input_read_named_object( dictDataIn, &meta_name_name );
		newType->name = (type_name *) type_stack_get_ptr( type_input_stack( dictDataIn ) );

		type_input_read_named_object( dictDataIn, &dictionary_definition_name );
		newType->structure = (type_envelop *) type_stack_get_ptr( type_input_stack( dictDataIn ) );
				
		newTypes[x] = newType;
	}

	//TypeMap mapSpec = new TypeMap( dict.getLibrary() );
	
	TRACE( "SET MAP - RESERVE TYPES" );
	// Step 2. Reserve unknown types and map types.
	for ( x = 0; x<size; x++ )
	{
		type_library * library = type_map_get_library( &coreMap->map );
		
		if ( type_library_get_state( library, newTypes[x]->name ) == TYPE_NOT_DEFINED )
		{
			int res = type_library_reserve( library, newTypes[x]->name );
			newTypes[x]->reserved = true;
			type_map_map( mapSpec, newTypes[x]->id, res );
		}
		else
		{
			newTypes[x]->reserved = false;
			type_map_map( mapSpec, newTypes[x]->id, type_library_get_id( library, newTypes[x]->name ) );
		}
	}

	TRACE( "SET MAP - COMPARE TYPES" );
	// Step 3. Compare known types with resolved structures.
	for ( x = 0 ; x<size; x++ )
	{
		if ( !newTypes[x]->reserved )
		{
			TRACE( newTypes[x]->name->name );
			type_buffer buffer;
			buffer.data = newTypes[x]->structure->data;
			buffer.size = newTypes[x]->structure->size;
			
			if ( !type_map_is_same( mapSpec, type_map_get_library_id( mapSpec, newTypes[x]->id ), newTypes[x]->name, &buffer, coreMap ) )
			{
				TRACE( "SET MAP - NOT SAME" );
				TRACE( newTypes[x]->name->name );
				return -1;				
			}
		}
	}
	
	TRACE( "SET MAP - READ STRUCTURES" );
	// Step 3. Read structures.
	// Don't register until all structures are read and type names
	// are resolved.
	for ( x = 0; x<size; x++ )
	{
		TRACE( "read structure" );
		TRACE( newTypes[x]->name->name );
		
		type_triple * t = newTypes[x];
		type_buffer b;
		b.data = t->structure->data;
		b.size = t->structure->size;
		t->definition = (type_element *) type_map_read_structure( mapSpec, coreMap, &b );
		if ( t->definition == NULL )
		{
			TRACE( "failed to read structure" );
			return -1;
		}
	}
	
	TRACE( "SET MAP - REGISTER STRUCTURES" );
	
	// Step 5. Register types with new structures.
	for ( int x = 0; x<size; x++ )
	{
		if ( newTypes[x]->reserved )
		{
			type_library * library = type_map_get_library( mapSpec );
			type_library_register( library, newTypes[x]->name, newTypes[x]->definition );
		}
	}
	
	return TYPE_OK;
}

static type_buffer * writeCore( reference_type_map * core )
{
	type_output_stream out;
	int * coreIds;
	int size;
	int x;
	type_buffer * buffer = type_malloc( sizeof( type_buffer ) );
	buffer_data writer_data;
	char * data = type_malloc( 1024 );
	int rc;
	/* write out the core elements. */

	buffer_data_init( &writer_data, data, 1024 );
	type_map_core_get_identifiers( map, &coreIds, &size );
	type_output_init( &out, &core->map, bufferWriter, &writer_data );
	
	/* write the number of entries. */
	rc = type_output_write_named_object( &out, &uint16_name, &size );
	if ( rc != TYPE_OK ) return NULL;

		
	for ( x=0 ; x<size; x++ )
	{
		int id = coreIds[x];
		type_name * name = type_map_get_name( &core->map, id );
		type_element * definition = type_map_get_structure( &core->map, id );
	
		rc = type_output_write_named_object( &out, &uint16_name, &id );
		if ( rc != TYPE_OK ) return NULL;
		rc = type_output_write_named_object( &out, &meta_name_name, name );
		if ( rc != TYPE_OK ) return NULL;		
		rc = type_output_write_named_object( &out, &dictionary_definition_name, definition );
		if ( rc != TYPE_OK ) return NULL;		
	}
	
	buffer->data = writer_data.data;
	buffer->size = writer_data.pos;

	return buffer;
}
